package com.example.viewtest;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.MotionEvent;

import java.util.Arrays;

public class MaterialEditText extends android.support.v7.widget.AppCompatEditText {
    private static final int OUT_OF_LINE = 0;
    private static final float LABEL_TEXT_SIZE = Utils.db2px(15);
    private static final float ERROR_TEXT_SIZE = Utils.db2px(15);

    private static final float BASIC_LINE_STROKE_WIDTH = Utils.db2px(2);
    private static final float BASIC_LINE_NORMAL_STROKE_WIDTH = Utils.db2px(1);
    private static final float BASIC_LINE_Y_OFFSET = Utils.db2px(3);


    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private int colorAccent;
    private int originalPaddingTop;
    private int originalPaddingBottom;
    private int originalPaddingRight;
    private int originalPaddingLeft;

    private boolean useHintLable;
    private ObjectAnimator animator = ObjectAnimator.ofFloat(MaterialEditText.this, "hintTextAlpha", 0, 1);
    private ObjectAnimator rightImgClickanimator = ObjectAnimator.ofFloat(MaterialEditText.this, "scaleRightImg", 1, 1.2f,1).setDuration(300);
    private float hintTextAlpha = 0;
    private boolean isShowHint = false;

    private boolean useErrorLabel;
    private String errorText;
    private int errorTextColor;
    private float hintLabelSize;
    private float errorTextPadding;

    private boolean isShowRightImg = false;
    private Drawable rightDrawable;
    private Rect rightImgRect;
    private OnRightImgClickListener rightImgClickListener;
    private int rightImagePadding;
    private int basicLineExtend = 0;
    private float scaleRightImg = 1;


    public MaterialEditText(Context context) {
        this(context, null);
    }

    public MaterialEditText(Context context, AttributeSet attrs) {
        this(context, attrs, android.support.v7.appcompat.R.attr.editTextStyle);
    }

    public MaterialEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttr(context, attrs);
        init();
    }

    private void initAttr(Context context, AttributeSet attrs) {
        int[] systemAttrs = {R.attr.colorAccent};
        int[] attrsArr = Arrays.copyOf(R.styleable.MaterialEditText, R.styleable.MaterialEditText.length + systemAttrs.length);
        int sysAttrIndexOffset = R.styleable.MaterialEditText.length;
        System.arraycopy(systemAttrs, 0, attrsArr, R.styleable.MaterialEditText.length, systemAttrs.length);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, attrsArr);
        colorAccent = typedArray.getColor(sysAttrIndexOffset, Color.BLACK);

        useHintLable = typedArray.getBoolean(R.styleable.MaterialEditText_useHintLabel, false);
        hintLabelSize = typedArray.getDimension(R.styleable.MaterialEditText_hintLabelSize, LABEL_TEXT_SIZE);

        useErrorLabel = typedArray.getBoolean(R.styleable.MaterialEditText_useErrorLabel, false);
        errorTextColor = typedArray.getColor(R.styleable.MaterialEditText_errorTextColor, colorAccent);
        errorTextPadding = typedArray.getDimension(R.styleable.MaterialEditText_errorTextPadding, 0) + BASIC_LINE_STROKE_WIDTH;

        int resourceId = typedArray.getResourceId(R.styleable.MaterialEditText_rightImage, -1);
        if (resourceId != -1) {
            isShowRightImg = true;
            rightDrawable = getResources().getDrawable(resourceId);
            int model = typedArray.getInt(R.styleable.MaterialEditText_rightImageModel, 0);
            basicLineExtend = model == OUT_OF_LINE ? 0 : rightDrawable.getIntrinsicWidth();
            rightImagePadding = (int) typedArray.getDimension(R.styleable.MaterialEditText_rightImagePadding, 0);
            if (model != OUT_OF_LINE)
                basicLineExtend += rightImagePadding;
        }

        typedArray.recycle();

    }

    private void init() {

        originalPaddingTop = getPaddingTop();
        originalPaddingBottom = getPaddingBottom();
        originalPaddingRight = getPaddingRight();
        originalPaddingLeft = getPaddingLeft();

        setBackgroundDrawable(null);

        if (useHintLable) {
            setPadding(getPaddingLeft(), (int) (originalPaddingTop + LABEL_TEXT_SIZE + originalPaddingTop),
                    getPaddingRight(), getPaddingBottom());

            addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {

                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {

                }

                @Override
                public void afterTextChanged(Editable s) {
                    if (!isShowHint && s.length() > 0) {
                        isShowHint = true;
                        animator.start();
                    } else if (isShowHint && s.length() == 0) {
                        isShowHint = false;
                        animator.reverse();
                    }
                }
            });
        }

        if (useErrorLabel)
            setPadding(getPaddingLeft(), getPaddingTop(),
                    getPaddingRight(), (int) (originalPaddingBottom + ERROR_TEXT_SIZE));

        if (isShowRightImg && rightDrawable != null) {
            setPadding(getPaddingLeft(), getPaddingTop(),
                    getPaddingRight() + rightDrawable.getIntrinsicWidth() + rightImagePadding, getPaddingBottom());
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (isShowRightImg && rightDrawable != null) {
            rightImgRect = new Rect(w - rightDrawable.getIntrinsicWidth() - originalPaddingRight, (h - rightDrawable.getIntrinsicHeight()) / 2,
                    w - originalPaddingRight, (h + rightDrawable.getIntrinsicHeight()) / 2);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                int x = (int) event.getX();
                int y = (int) event.getY();
                if (rightImgClickListener != null && rightImgRect != null && rightImgRect.contains(x, y)) {
                    rightImgClickanimator.start();
                    rightImgClickListener.onRightImgClick();
                    return true;
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        drawBasicLine(canvas);

        if (useHintLable) {
            drawHintLabel(canvas);
        }

        if (isShowRightImg)
            drawRightDrawable(canvas);

        if (useErrorLabel && null != errorText) {
            drawErrorLabel(canvas);
        }

    }

    private void drawRightDrawable(Canvas canvas) {
        if (rightDrawable == null)
            return;
        canvas.save();
        canvas.scale(scaleRightImg,scaleRightImg,rightImgRect.centerX(),rightImgRect.centerY());
        rightDrawable.setAlpha((int) (scaleRightImg*(255/1.2f)));
        rightDrawable.setBounds(rightImgRect);
        rightDrawable.draw(canvas);
        canvas.restore();
    }

    private void drawErrorLabel(Canvas canvas) {
        int color = paint.getColor();
        float textSize = paint.getTextSize();

        paint.setColor(errorTextColor);
        paint.setTextSize(ERROR_TEXT_SIZE);
        canvas.drawText(errorText, getPaddingLeft(), getHeight() - originalPaddingBottom + errorTextPadding, paint);

        paint.setTextSize(textSize);
        paint.setColor(color);
    }

    private void drawBasicLine(Canvas canvas) {
        int color = paint.getColor();
        float strokeWidth = paint.getStrokeWidth();

        if (isFocused()) {
            paint.setColor(colorAccent);
            paint.setStrokeWidth(BASIC_LINE_STROKE_WIDTH);
        } else {
            paint.setColor(color);
            paint.setStrokeWidth(BASIC_LINE_NORMAL_STROKE_WIDTH);
        }

        canvas.drawLine(getPaddingLeft(), getHeight() - getPaddingBottom() + BASIC_LINE_Y_OFFSET,
                getWidth() - getPaddingRight() + basicLineExtend, getHeight() - getPaddingBottom() + BASIC_LINE_Y_OFFSET, paint);

        paint.setColor(color);
        paint.setStrokeWidth(strokeWidth);
    }

    private void drawHintLabel(Canvas canvas) {
        if (getHint() == null)
            return;

        int alpha = paint.getAlpha();
        int color = paint.getColor();
        float textSize = paint.getTextSize();

        paint.setTextSize(hintLabelSize);
        paint.setColor(colorAccent);
        paint.setAlpha((int) (hintTextAlpha * 255));
        float offset = originalPaddingTop * (1 - hintTextAlpha);
        canvas.drawText(getHint().toString(), getPaddingLeft(), offset + originalPaddingTop + LABEL_TEXT_SIZE, paint);

        paint.setTextSize(textSize);
        paint.setAlpha(alpha);
        paint.setColor(color);
    }

    public float getHintTextAlpha() {
        return hintTextAlpha;
    }

    public void setHintTextAlpha(float hintTextAlpha) {
        this.hintTextAlpha = hintTextAlpha;
        postInvalidate();
    }

    public String getErrorText() {
        return errorText;
    }

    public void setErrorText(String errorText) {
        if (!useErrorLabel)
            return;
        this.errorText = errorText;
        postInvalidate();
    }

    public void clearError() {
        setErrorText("");
    }

    public interface OnRightImgClickListener {
        void onRightImgClick();
    }

    public void setRightImgClickListener(OnRightImgClickListener rightImgClickListener) {
        this.rightImgClickListener = rightImgClickListener;
    }

    public float getScaleRightImg() {
        return scaleRightImg;
    }

    public void setScaleRightImg(float scaleRightImg) {
        this.scaleRightImg = scaleRightImg;
        postInvalidate();
    }
}
